﻿
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

using System.Xml;
using System.Xml.XPath;

using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Core;

namespace BackupRestoreDeploy
{
	public class DataSyncBizRules : Syrinx.BaseBizRules
	{
		public void doDataSync(IApplication app, IRuleContext context, Swaf.BGuid.Guid syncInfoId)
		{
			FlexiMap m = new FlexiMap();
			IResults rc = null;
			DateTime startOfSync = DateTime.UtcNow;
			//Step 1: Get sync info.
			m.put("id", syncInfoId);
			IBizObj syncInfo = app.appData.get("DA.BackupRestoreDeploy.GetSiteSync", m) as IBizObj;
			if (syncInfo != null)
			{
				bool downloadOnly = false;
				bool uploadOnly = false;
				string direction = (string)syncInfo.get("direction");
				if (Cmn.IsEmpty(direction))
					direction = "Both";
				string remoteSiteUrl = (string)syncInfo.get("serverUrl");

				IBizObj backupInfo = (IBizObj)syncInfo.get("dataOptions");
				//IBizObj backupInfo = app.bizObjMgr.create("CompanyBackup");
				//backupInfo.put("fullCompanyBackup", false);
				//backupInfo.put("includeChildren", false);
				//backupInfo.put("includeSitePages", false);
				//backupInfo.put("includeImageCache", true);
				//backupInfo.put("articleLevel", "full");
				//backupInfo.put("backupSince", syncInfo.get("lastGoodSync"));
				IBizObj localBackupInfo = (IBizObj)backupInfo.Clone();

				rc = context.RuleGroup.execute(context, "getCompany", syncInfo.get("localCompanyId"));
				IBizObj localCompany = rc.CallResults as IBizObj;

				IRuleGroup rg = null;
				if (string.Compare(direction, "upload", true) != 0)
				{
					//Step 2: async ask for backup on remote server
					rg = app.ruleMgr.remoteExecuteAsync("createCompanyBackup", remoteSiteUrl, null, "", backupInfo);
				}
				else
					uploadOnly = true;

				if (string.Compare(direction, "download", true) != 0)
				{
					//Step 3: async ask for backup on this server
					((IBizObjList)localBackupInfo.get("backupCompanies")).Add(localCompany);
					if (rg == null)
						rg = app.ruleMgr.executeAsync("createCompanyBackup", null, "", localBackupInfo);
					else
						rg.executeAsync("createCompanyBackup", null, "", localBackupInfo);
				}
				else
					downloadOnly = true;

				//Step 4: when step 2&3 complete, ask for remote server to restore this server's backup
				if(rg != null)
					rg.waitForCompletion(true);

				string baseDir = app.resMgr.resolve(Cmn.pathCombine(app.resMgr.getMacro("BaseBackupStore"), (string)ActiveCompany.get("shortName")));
				if (!downloadOnly)
				{
					localBackupInfo = rg.getCallResults(1) as IBizObj;
					string localBackupFileName = Cmn.pathCombine(baseDir, (string)localBackupInfo.get("id"));
					bool hasLocalChanges = (int)localBackupInfo.get("filesInBackup") > 1;

					if (hasLocalChanges)
					{
						IBizObj remoteCompany = app.bizObjMgr.create("Company");
						remoteCompany.put("id", syncInfo.get("remoteCompanyId"));
						((IBizObjList)localBackupInfo.get("restoreCompanies")).Add(remoteCompany);
						rg.remoteExecuteAsync("uploadClientSyncBackup", remoteSiteUrl, null, "", localBackupInfo, File.ReadAllBytes(localBackupFileName));
					}
				}

				if (!uploadOnly)
				{
					//Step 5: ask for this server to restore remote server's backup.
					try
					{
						IBizObj backupResults = rg.CallResults as IBizObj;
						if (backupResults != null)
						{
							bool hasRemoteChanges = (int)backupResults.get("filesInBackup") > 1;
							if (hasRemoteChanges)
							{
								Uri site = new Uri(remoteSiteUrl);
								string baseServerBackupFileName = (string)backupResults.get("id");
								string reqtype = "http://"; //TODO: check if it should be secure and if so use https
								string shortName = "";
								IBizObj remCo = backupResults.get("backupCompanies.[0]") as IBizObj;
								if (remCo != null)
									shortName = "/" + remCo.get("shortName");
								string backupUrl = string.Format("{0}{1}:{2}/filesys{4}/_sitebackups/{3}", reqtype, site.Host, site.Port, baseServerBackupFileName, shortName);
								//HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(backupUrl));
								//WebResponse resp = req.GetResponse();
								string serverBackupFileName = Cmn.pathCombine(baseDir, "server" + baseServerBackupFileName);
								WebClient wc = new WebClient();
								wc.DownloadFile(backupUrl, serverBackupFileName);
								ZipFile zip = new ZipFile(serverBackupFileName);
								try
								{
									if (localCompany != null)
									{
										((IBizObjList)backupResults.get("restoreCompanies")).Add(localCompany);
										rg.executeAsync("executeCompanyRestoreFromZip", null, "", new object[] { backupResults, zip });
									}
								}
								finally
								{
									FlexiMap m2 = new FlexiMap();
									rg.waitForCompletion(true);
									m2.put("lastGoodSync", startOfSync);
									app.appData.put("DA.BackupRestoreDeploy.SetSiteSyncLastGoodSync", m2);
									zip.Close();
									File.Delete(serverBackupFileName);
								}
							}
						}
					}
					finally
					{
						//File.Delete(localBackupFileName);
					}
				}

			}
		}

		public void uploadClientSyncBackup(IApplication app, IRuleContext context, IBizObj backupInfo, byte[] backupBytes)
		{
			string baseDir = app.resMgr.resolve(Cmn.pathCombine(app.resMgr.getMacro("BaseBackupStore"), (string)ActiveCompany.get("shortName")));
			string backupFileName = Cmn.pathCombine(baseDir, "client" + backupInfo.get("id"));
			File.WriteAllBytes(backupFileName, backupBytes);
			ZipFile zip = new ZipFile(backupFileName);
			try
			{
				IResults rc = context.RuleGroup.execute(context, "getCompany", backupInfo.get("restoreCompanies.[0].id"));
				IBizObj localCompany = rc.CallResults as IBizObj;
				if (localCompany != null)
				{
					IBizObjList resCompanies = (IBizObjList)backupInfo.get("restoreCompanies");
					resCompanies.Clear();
					resCompanies.Add(localCompany);
					context.RuleGroup.execute("executeCompanyRestoreFromZip", backupInfo, zip);
				}
			}
			finally
			{
				zip.Close();
				File.Delete(backupFileName);
			}
		}

		public IBizObj getSiteSyncDetail(IApplication app, IRuleContext context, Swaf.BGuid.Guid syncId)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", syncId);
			IBizObj sync = app.appData.get("DA.BackupRestoreDeploy.GetSiteSync", m) as IBizObj;
			sync.put("dataOptions.backupSince", sync.get("lastGoodSync"));
			return sync;
		}

		public IBizObjList getSiteSyncs(IApplication app, IRuleContext context)
		{
			FlexiMap m = new FlexiMap();
			m.put("companyId", ActiveCompanyId);
			IBizObjList syncs = app.appData.get("DA.BackupRestoreDeploy.GetSiteSyncs", m) as IBizObjList;
			return syncs;
		}

		public void updateSiteSync(IApplication app, IRuleContext context, IBizObj siteSync)
		{
			if (siteSync.isNew)
			{
				siteSync.put("id", new Swaf.BGuid.Guid());
				siteSync.put("companyId", ActiveCompanyId);
				app.appData.put("DA.BackupRestoreDeploy.AddSiteSync", siteSync);
			}
			else
				app.appData.put("DA.BackupRestoreDeploy.UpdateSiteSync", siteSync);
		}

		public void deleteSiteSync(IApplication app, IRuleContext context, Swaf.BGuid.Guid id)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", id);
			app.appData.put("DA.BackupRestoreDeploy.DeleteSiteSync", m);
		}

		public IBizObjList getSyncModeOptions(IApplication app, IRuleContext context)
		{
			return app.appData.get("BackupRestoreDeploy.SyncModeOptions") as IBizObjList;
		}

		public IBizObjList getSyncDirectionOptions(IApplication app, IRuleContext context)
		{
			return app.appData.get("BackupRestoreDeploy.DirectionOptions") as IBizObjList;
		}

		public IBizObjList getConflictModeOptions(IApplication app, IRuleContext context)
		{
			return app.appData.get("BackupRestoreDeploy.ConflictModeOptions") as IBizObjList;
		}

		public IBizObjList getLocalCompaniesForDataSync(IApplication app, IRuleContext context)
		{
			IResults rc = context.RuleGroup.execute(context, "getChildCompanies", ActiveCompanyId, true);
			IBizObjList companies = rc.CallResults as IBizObjList;
			return companies;
		}

		public IBizObjList getRemoteSiteCompaniesForDataSync(IApplication app, IRuleContext context, string remoteSiteUrl)
		{
			IResults rc = app.ruleMgr.remoteExecute("getLocalCompaniesForDataSync", remoteSiteUrl);
			IBizObjList companies = rc.CallResults as IBizObjList;
			return companies;
		}
	}
}
